var gamejs = require("gamejs");
var geMod = require("js/gameEntity");//gameEntity module

/**
 * Checks if the given GameEntity objects collide each others,
 * whatever shape they have.
 *
 * @param {GameEntity} geA The first GameEntity object to check.
 * @param {GameEntity} geB The second GameEntity object to check.
 * @return {boolean} True if the given objects collide, false otherwise.
 * @throws {TypeError} If the given objects aren't GameEntity instances.
 * @throws {RangeError} If the getShape() of at least one of the given objects
 * didn't return a valid shape.
 */
var collideGeneric = exports.collideGeneric = function (geA, geB) {
	if(( ! (geA instanceof geMod.GameEntity)) || ( ! (geB instanceof geMod.GameEntity))) {
		throw new TypeError("The given objects aren't GameEntity instances");
	}
	
	var shA = geA.getShape();
	var shB = geB.getShape();
	
	if(((shA != SHAPE_RECTANGULAR) && (shA != SHAPE_CIRCULAR)) ||
		((shB != SHAPE_RECTANGULAR) && (shB != SHAPE_CIRCULAR))) {
		throw new RangeError("The given objects haven't valid shape");
	}
	
	if(shA == shB) {
		if(shA == SHAPE_RECTANGULAR) {
			return gamejs.sprite.collideRect(geA, geB);
		}
		else {
			return gamejs.sprite.collideCircle(geA, geB);
		}
	}
	else {
		//here we check for collisions between two
		//gameEntity with a different shape
		var realRect;
		var adaptedRect;
		var rad = 0;
		var minDim = 0;
		if(shA == SHAPE_RECTANGULAR) {
			//gameEntityA is rectangular
			//-> gameEntityB must be circular
			realRect = geA.getRect();
			var center = geB.getCenter();
			rad = geB.getObstructiveRadius();
			var diameter = rad * 2;
			var dims = [diameter, diameter];
			var topleft = calcTopleft(center, dims);
			adaptedRect = new gamejs.Rect(topleft, dims);
		}
		else {
			//gameEntityA is circular
			//-> gameEntityB must be rectangular
			realRect = geB.getRect();
			var center = geA.getCenter();
			rad = geA.getObstructiveRadius();
			var diameter = rad * 2;
			var dims = [diameter, diameter];
			var topleft = calcTopleft(center, dims);
			adaptedRect = new gamejs.Rect(topleft, dims);
		}
		
		var collide = realRect.collideRect(adaptedRect);
		if (collide) {
			// the two rects collide, this means that the circle and the rect
			// collide, unless the collision is in the corner
			var circleCenter = adaptedRect.center;
			var dis = 0;
			if (circleCenter[X] < realRect.left) {
				if (circleCenter[Y] < realRect.top) {
					// near the topleft corner
					dis = distance(circleCenter, realRect.topleft);
				} else if (circleCenter[Y] > realRect.bottom) {
					// near the bottomleft corner
					dis = distance(circleCenter, realRect.bottomleft);
				}
			} else if (circleCenter[X] > realRect.right) {
				if (circleCenter[Y] < realRect.top) {
					// near the topright corner
					dis = distance(circleCenter, realRect.topright);
				} else if (circleCenter[Y] > realRect.bottom) {
					// near the bottomright corner
					dis = distance(circleCenter, realRect.bottomright);
				}
			}
			
			return (dis <= rad);
		}
		else {
			return false;
		}
	}
};

/**
 * Draws a Surface into another one by centering it to a point.
 *
 * @param {gamejs.Surface} src The Surface object to draw.
 * @param {gamejs.Surface} dest The Surface object where to draw.
 * @param {[number, number]} point The point on which to center, in [x, y] format.
 * @throws {TypeError} If coordinates aren't in the correct form [number, number].
 */
var drawAtMiddle = exports.drawAtMiddle = function (src, dest, point) {
	if( ! areCoordinates(point)) {
		throw new TypeError("Coordinates not in the correct form");
	}
	var dims = src.getSize();
	var topleft = [Math.round(point[X] - dims[WIDTH] / 2), Math.round(point[Y] - dims[HEIGHT] / 2)];
	dest.blit(src, topleft);
};

/**
 * Checks if an index is valid to access an array.
 *
 * @param {number} index The index to check.
 * @param {Array} array The array to be accessed.
 * @return {boolean} True if the index can be safely used to access
 * the given array, false otherwise.
 */
var validIndex = exports.validIndex = function (index, array) {
	return (index >= 0 && index < array.length);
};

//////// METHODS FOR COORDINATES MANAGEMENT ////////

/**
 * Checks if the given object represents some coordinates.
 *
 * @param {Object} obj The object to check.
 * @return {boolean} True if the given object is an Array of two number, false otherwise.
 */
var areCoordinates = exports.areCoordinates = function (obj) {
	return ((obj.length == 2) &&
		(typeof obj[X] == "number") && (typeof obj[Y] == "number") &&
		( ! isNaN(obj[X])) && ( ! isNaN(obj[Y])));
};

/**
 * Calculates the topleft corner of a rectangle described by center
 * and dimensions.
 *
 * @param {[number, number]} center The center of the rectangle,
 * in [x, y] format.
 * @param {[number, number]} dims The dimensions of the rectangle,
 * in [width, height] format.
 * @return {[number,number]} The coordinates of the topleft corner
 * of the given rectangle.
 * @throws {TypeError} If coordinates aren't in the correct form [number, number].
 */
var calcTopleft = exports.calcTopleft = function (center, dims) {
	if( ! areCoordinates(center)) {
		throw new TypeError("Coordinates not in the correct form");
	}
	
	return [Math.round(center[X] - dims[WIDTH] / 2), Math.round(center[Y] - dims[HEIGHT] / 2)];
};

/**
 * Calculates the distance between two points.
 *
 * @param {[number, number]} point1 The coordinates of the first point,
 * in [x, y] format.
 * @param {[number, number]} point2 The coordinates of the second point,
 * in [x, y] format.
 * @return {Number} The distance between point1 and point2.
 * @throws {TypeError} If coordinates aren't in the correct form [number, number].
 */
var distance = exports.distance = function (point1, point2) {
	if(( ! areCoordinates(point1)) || ( ! areCoordinates(point2))) {
		throw new TypeError("Coordinates not in the correct form");
	}
	var squareX = Math.pow(point1[X] - point2[X], 2);
	var squareY = Math.pow(point1[Y] - point2[Y], 2);
	
	return Math.sqrt(squareX + squareY);
};

/////// GAME MANAGEMENT /////////
/**
 * Preload all resources needed by the game
 */
var preload = exports.preload = function() {
	gamejs.preload([
		// images for GameEntity
		IMAGE_ROOT + "obstacle0.png",
		IMAGE_ROOT + "obstacle1.png",
		IMAGE_ROOT + "obstacle2.png",
		IMAGE_ROOT + "radar.png",
		IMAGE_ROOT + "radar1.png",
		IMAGE_ROOT + "turret.png",
		IMAGE_ROOT + "turret1.png",
		IMAGE_ROOT + "turrets/laserturret.png",
		IMAGE_ROOT + "turrets/rocketturret.png",
		IMAGE_ROOT + "turrets/slowingturret.png",
		IMAGE_ROOT + "turrets/gunturret.png",
		IMAGE_ROOT + "turrets/gunturret1.png",
		IMAGE_ROOT + "turrets/rocket.png",
		IMAGE_ROOT + "explosion.png",
		IMAGE_ROOT + "turrets/mine.png",
		IMAGE_ROOT + "enemies/enemyPink.png",
		IMAGE_ROOT + "enemies/enemyRed.png",
		IMAGE_ROOT + "enemies/squaredEnemy.png",
		IMAGE_ROOT + "enemies/squaredDetectedEnemy.png",
		// images for NightActions
		IMAGE_ROOT + "nightActions/flare.png",
		IMAGE_ROOT + "nightActions/cannon.png",
		IMAGE_ROOT + "nightActions/bomb.png",
		// Map backgrounds
		IMAGE_ROOT + "backgroundDay.png",
		IMAGE_ROOT + "backgroundNight.png",
		// images for DayScene and NightScene
		IMAGE_ROOT + "hudBackground.png",
		IMAGE_ROOT + "toNight.png",
		IMAGE_ROOT + "disabledToNight.png",
		IMAGE_ROOT + "build.png",
		IMAGE_ROOT + "disabledBuild.png",
		IMAGE_ROOT + "sell.png",
		IMAGE_ROOT + "disabledSell.png",
		IMAGE_ROOT + "upgrade.png",
		IMAGE_ROOT + "disabledUpgrade.png",
		IMAGE_ROOT + "showInfo.png",
		IMAGE_ROOT + "disabledShowInfo.png",
		IMAGE_ROOT + "pause.png",
		// images for PauseScene
		IMAGE_ROOT + "pauseScene.png",
		// images for StoreScene
		IMAGE_ROOT + "backArrow.png",
		IMAGE_ROOT + "storeInfo.png" ,
		IMAGE_ROOT + "storeBuy.png",
		// images for other scenes
		IMAGE_ROOT + "gameOver.png",
		IMAGE_ROOT + "levelCleared.png",
		IMAGE_ROOT + "missionCompleted.png",
		IMAGE_ROOT + "startBackground.png",
		IMAGE_ROOT + "main.png",
		IMAGE_ROOT + "loadingNightBackground.png",
		// tutorial
		IMAGE_ROOT + "tutorial1.png",
		IMAGE_ROOT + "tutorial2.png",
		IMAGE_ROOT + "tutorial3.png",

		// Sounds preload
		SOUND_ROOT + "DayTheme.ogg",
		SOUND_ROOT + "NightTheme.ogg",
		SOUND_ROOT + "explosion0.ogg",
		SOUND_ROOT + "explosion1.ogg",
		SOUND_ROOT + "laser0.ogg",
		SOUND_ROOT + "laser1.ogg",
		SOUND_ROOT + "missile0.ogg",
		SOUND_ROOT + "missile1.ogg",
		SOUND_ROOT + "missile2.ogg"
	]);
};